using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using log4net;
using log4net.Config;

namespace Cz.CzLog
{
    public class CzLog4Net : ICzLog
    {
        [CzServiceField]
        private ICzConfig config;
        
        private ILog _logger;
        private bool showStack = false;
        
        public void Start()
        {
            var configPath = AppDomain.CurrentDomain.BaseDirectory + "log4net.config";
            
            // 初始化Log配置
            Assembly.GetExecutingAssembly().CreateFile(configPath, "CzLog.log4net.config");
            var logCfg = new FileInfo(configPath);

            XmlConfigurator.ConfigureAndWatch(logCfg);
            
            // 创建日志记录组件实例 
            _logger  =  LogManager.GetLogger("CzLog");
            ReLoadConfig();
            config.OnChange += ReLoadConfig;
            
            // TODO 未捕获异常目前还是捕获不到
            
            // 添加未捕获异常
            AppDomain.CurrentDomain.UnhandledException -= CurrentDomain_UnhandledException;
            AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
            
            Info("CzLog4Net Start");
        }

        public void Info(string title, string message = null)
        {
            _logger.Info(FormatMessage(title, message));
        }

        public void Warn(string title, string message)
        {
            _logger.Warn(FormatMessage(title, message));
        }

        public void Error(string title, string message)
        {
            _logger.Error(FormatMessage(title, message));
        }

        public void Error(string title, string message, Exception exception)
        {
            _logger.Error(title + "\n" + message, exception);
        }
        
        private void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
        {
            // 后续处理，保存或输出
            Error(sender.ToString(), "", e.ExceptionObject as Exception);
        }
        
        private object FormatMessage(string title, string message)
        {
            if (showStack)
            {
                var builder = new StringBuilder();
                var trace = new StackTrace();
                var frameList = trace.GetFrames();
                var realFrameList = frameList
                    .Where(i => 
                        i.GetMethod().DeclaringType != typeof(CzLog4Net) && 
                        i.GetFileLineNumber() >= 0);
                builder.AppendLine(title);
                if(message != null)
                    builder.AppendLine(message);
                if (realFrameList.Any())
                {
                    realFrameList = realFrameList.Reverse();
                    foreach (var frame in realFrameList)
                    {
                        var method = frame.GetMethod();
                        builder.AppendFormat("> {0}.{1}", method.DeclaringType, method).AppendLine();
                    }

                    builder.Length--;
                }
                return builder.ToString();
            }

            return message == null ? title : title + "\n" + message + "\n";
        }
        
        private void ReLoadConfig()
        {
            showStack = config.GetWithDefault(false, "log", "show_stack");
        }
    }
}